package sameClasses;

import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.encoders.Base64;

//import server.Users;

public class Crypter {

  public Crypter() {
  }
  
  // derive the password and return a secretkey object for usage in the encryption and decryption process
  private static SecretKey buildSecret(char[] password, byte[] salt) {
    SecretKeyFactory factory;
    SecretKey secret = null;
    try {
      factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
      KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
      SecretKey tmp = factory.generateSecret(spec);
      secret = new SecretKeySpec(tmp.getEncoded(), "AES");
      return secret;
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
    }
    password = null;
    return secret;
  }
  
  // only derive the password and return a byte-array with the hash
  public static byte[] buildSecretBytes(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
    SecretKey tmp = factory.generateSecret(spec);
    return tmp.getEncoded();
  }

  public static EncryptedData encrypt(char[] password, byte[] salt, byte[] rawdata) {
    Cipher cipher; 
    try {
      cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      cipher.init(Cipher.ENCRYPT_MODE, buildSecret(password, salt));
      AlgorithmParameters params = cipher.getParameters();
      byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
      byte[] ciphertext = cipher.doFinal(rawdata);
      return new EncryptedData(iv, ciphertext);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (NoSuchPaddingException e) {
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
      e.printStackTrace();
    } catch (BadPaddingException e) {
      e.printStackTrace();
    } catch (InvalidParameterSpecException e) {
      e.printStackTrace();
    }
    return new EncryptedData(new byte[0], new byte[0]);
  }

  public static byte[] decrypt(char[] password, byte[] salt, EncryptedData encdata) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException {
    Cipher cipher;
    // Decrypt the message, given derived key and initialization vector.
      cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      cipher.init(Cipher.DECRYPT_MODE, buildSecret(password, salt), new IvParameterSpec(encdata.getIV()));
      byte[] decData = cipher.doFinal(encdata.getEncData());
      return decData;
  }
  
  public static char[] bbCharPw(char[] input) {
    for(int i = 0; i<input.length; i++) {
      input[i] = '*';
    }
    return input;
  }
  /*
  public static void main(String[] args) {
    //String bla = new String("test");
    User user = new User("test", "test");
    Users users = new Users();
    users.add(user);
    System.out.println("This is the data:"+users.getList());
    EncryptedData ed = encrypt("test".toCharArray(), user.getSalt(), Bytehandler.pluginDataToBytes(users));
    System.out.println("This is the encrypted data:"+ed);
    Users users2 = (Users) Bytehandler.BytesToPlugindata(decrypt("test".toCharArray(), user.getSalt(), ed));
    System.out.println(users2.getList());
  }*/
}